﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.Entity;
using System.Data.Entity.Core.Mapping;
using System.Data.Entity.Core.Metadata.Edm;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.ModelConfiguration.Conventions;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using PlutoStudio.EntityFramework.Extensions;
namespace PlutoStudio.EntityFramework.Primitives
{
    public abstract class ContextBase : DbContext, ISqlContext, IDbContext
    {
        public ContextBase()
           : base("default") { }

        public ContextBase(string nameOrConnectionString)
            : base(nameOrConnectionString) { }

        public ContextBase(DbConnection existingConnection)
            : base(existingConnection, true)
        {
        }

        protected abstract IEnumerable<IEntityMapper> EntityMappers { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();

            if (EntityMappers == null)
            {
                return;
            }

            foreach (var mapper in EntityMappers)
            {
                mapper.RegistTo(modelBuilder.Configurations);
            }
        }

        int ISqlContext.ExecuteSqlCommand(string sql, params object[] parameters)
        {
            return Database.ExecuteSqlCommand(sql, parameters);
        }

        int ISqlContext.ExecuteSqlCommand(TransactionalBehavior transactionalBehavior, string sql, params object[] parameters)
        {
            return Database.ExecuteSqlCommand(transactionalBehavior, sql, parameters);
        }

        Task<int> ISqlContext.ExecuteSqlCommandAsync(string sql, params object[] parameters)
        {
            return Database.ExecuteSqlCommandAsync(sql, parameters);
        }

        Task<int> ISqlContext.ExecuteSqlCommandAsync(string sql, CancellationToken cancellationToken, params object[] parameters)
        {
            return Database.ExecuteSqlCommandAsync(sql, cancellationToken, parameters);
        }

        Task<int> ISqlContext.ExecuteSqlCommandAsync(TransactionalBehavior transactionalBehavior, string sql, params object[] parameters)
        {
            return Database.ExecuteSqlCommandAsync(transactionalBehavior, sql, parameters);
        }

        Task<int> ISqlContext.ExecuteSqlCommandAsync(TransactionalBehavior transactionalBehavior, string sql, CancellationToken cancellationToken, params object[] parameters)
        {
            return Database.ExecuteSqlCommandAsync(transactionalBehavior, sql, cancellationToken, parameters);
        }

        DbRawSqlQuery ISqlContext.SqlQuery(Type elementType, string sql, params object[] parameters)
        {
            Database.SqlQueryForDynamic(sql, parameters);
            return Database.SqlQuery(elementType, sql, parameters);
        }
        DbRawSqlQuery ISqlContext.SqlQuery(string sql, params object[] parameters)
        {
            Database.CommandTimeout = 360000;
            return Database.SqlQueryForDynamic(sql, parameters);
        }

        DbRawSqlQuery<TElement> ISqlContext.SqlQuery<TElement>(string sql, params object[] parameters)
        {
            return Database.SqlQuery<TElement>(sql, parameters);
        }

        DbParameter ISqlContext.CreateParameter(string name, object value)
        {
            using (var command = Database.Connection.CreateCommand())
            {
                var parameter = command.CreateParameter();
                parameter.ParameterName = name;
                parameter.Value = value;
                return parameter;
            }
        }

        IEnumerable<DbParameter> ISqlContext.CreateParameters(IDictionary<string, object> parameters)
        {
            using (var command = Database.Connection.CreateCommand())
            {
                return parameters.Select(i =>
                {
                    var parameter = command.CreateParameter();
                    parameter.ParameterName = i.Key;
                    parameter.Value = i.Value;
                    return parameter;
                });
            }
        }

        DbContextTransaction ISqlContext.BeginTransaction()
        {
            return Database.BeginTransaction();
        }

        DbContextTransaction ISqlContext.BeginTransaction(IsolationLevel isolationLevel)
        {
            return Database.BeginTransaction(isolationLevel);
        }

        void IDbContext.Preinitialize()
        {
            var objectContext = ((IObjectContextAdapter)this).ObjectContext;
            var mappingCollection = (StorageMappingItemCollection)objectContext.MetadataWorkspace.GetItemCollection(DataSpace.CSSpace);
            mappingCollection.GenerateViews(new List<EdmSchemaError>());

        }
    }
}
